package com.mlh.aop;

import com.alibaba.fastjson.JSON;
import com.mlh.annotation.SystemLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MemberSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author 缪林辉
 * @date 2024/3/17 10:36
 * @DESCRIPTION
 */
@Aspect
@Slf4j
@Component
//Log切面类
public class LogAop {
    //设置切入点
    @Pointcut("@annotation(com.mlh.annotation.SystemLog)")
    public void pt(){}

    //aop要写的好就要掌握好ProceedingJoinPoint joinPoint 的使用，因为他是aop唯一的参数
    //切点的很多信息都要通过它来获取
    @Around("pt()")
    public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        //原方法返回值
        Object res;
        try{
            handleBefore(joinPoint);
            //这里出现异常要抛出去，如果在这里把异常处理了，全局异常就相当于没用了
            res=joinPoint.proceed();
            handleAfter(res);
        }finally {
            // 结束后换行
            //System.lineSeparator()这里是获取系统的换行符输出
            //因为windows和linux的换行符是不一样的，为了代码的可适用性需要这样写
            log.info("=======End=======" + System.lineSeparator());
        }
       return res;//把原方法返回值返回去
    }

    private void handleBefore(ProceedingJoinPoint joinPoint) {
        //获取HttpServletRequest对象 得到里面url
        //浏览器http包的信息都被封装到HttpServletRequest对象中
        ServletRequestAttributes requestAttributes
                = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        log.info("=======Start=======");
        // 打印请求 URL
        //url被封装在request对象中
        log.info("URL : {}",request.getRequestURL());
        // 打印描述信息
        //获取被增强方法上的注解
        SystemLog systemLog = getSystemLog(joinPoint);
        log.info("BusinessName : {}", systemLog.BusinessName());
        // 打印 Http method
        log.info("HTTP Method : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        //这里先获取类的全路径名  在获取具体的controller的方法名
        log.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(),
                ((MethodSignature) joinPoint.getSignature()).getName());
        // 打印请求的 IP
        log.info("IP : {}",request.getRemoteHost());
        // 打印请求入参
        log.info("Request Args : {}", JSON.toJSONString(joinPoint.getArgs()));
    }

    //获取切点方法上的注解
    private SystemLog getSystemLog(ProceedingJoinPoint joinPoint) {
       MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        SystemLog annotation = methodSignature.getMethod().getAnnotation(SystemLog.class);
        return annotation;
    }

    private void handleAfter(Object res) {
        // 打印出参
        log.info("Response : {}",JSON.toJSONString(res));
    }
}
